home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / haeberli / fonttools / afm2tfm.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  35KB  |  1,296 lines

  1. /*
  2.  * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  *   This program converts AFM files to TeX TFM files, and optionally
  19.  *   to TeX VPL files that retain all kerning and ligature information.
  20.  *   Both files make the characters not normally encoded by TeX available
  21.  *   by character codes greater than 127.
  22.  */
  23.  
  24. /*   (Modified by Don Knuth from Tom Rokicki's pre-VPL version.) */
  25.  
  26. #include <stdio.h>
  27. #ifdef SYSV
  28. #include <string.h>
  29. #else
  30. #ifdef VMS
  31. #include <string.h>
  32. #else
  33. #include <strings.h>
  34. #endif
  35. #endif
  36. #include <math.h>
  37.  
  38. char *texencoding[] = {
  39.    "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma",
  40.    "Upsilon", "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle",
  41.    "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", "acute",
  42.    "caron", "breve", "macron", "ring", "cedilla", "germandbls", "ae", "oe",
  43.    "oslash", "AE", "OE", "Oslash", "space", "exclam", "quotedbl", "numbersign",
  44.    "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
  45.    "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one",
  46.    "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon",
  47.    "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C",
  48.    "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
  49.    "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash",
  50.    "bracketright", "circumflex", "underscore", "quoteleft", "a", "b", "c", "d",
  51.    "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
  52.    "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
  53.    "tilde", "dieresis" } ;
  54. /*
  55.  *   The above layout corresponds to TeX Typewriter Type and is compatible
  56.  *   with TeX Text because the position of ligatures is immaterial.
  57.  */
  58.  
  59. /*
  60.  *   This is what we store Adobe data in.
  61.  */
  62. struct adobeinfo {
  63.    struct adobeinfo *next ;
  64.    int adobenum, texnum, width ;
  65.    char *adobename ;
  66.    int llx, lly, urx, ury ;
  67.    struct lig *ligs ;
  68.    struct kern *kerns ;
  69.    struct pcc *pccs ;
  70.    int wptr, hptr, dptr, iptr ;
  71. } *adobechars, *adobeptrs[256], *texptrs[256],
  72.   *uppercase[256], *lowercase[256] ;
  73. struct lig {
  74.    struct lig *next ;
  75.    char *succ, *sub ;
  76. } ;
  77. struct kern {
  78.    struct kern *next ;
  79.    char *succ ;
  80.    int delta ;
  81. } ;
  82. struct pcc {
  83.    struct pcc *next ;
  84.    char * partname ;
  85.    int xoffset, yoffset ;
  86. } ;
  87.  
  88. FILE *afmin, *vplout, *tfmout ;
  89. char inname[200], outname[200] ; /* names of input and output files */
  90. char buffer[255]; /* input buffer (modified while parsing) */
  91. char obuffer[255] ; /* unmodified copy of input buffer */
  92. char *param ; /* current position in input buffer */
  93. char *fontname = "Unknown" ;
  94. char *codingscheme = "Unspecified" ;
  95. float italicangle = 0.0 ;
  96. char fixedpitch ;
  97. char makevpl ;
  98. int xheight = 400 ;
  99. int fontspace ;
  100. int bc, ec ;
  101. long cksum ;
  102. float efactor = 1.0, slant = 0.0 ;
  103. double newslant ;
  104. char titlebuf[100] ;
  105.  
  106. void
  107. error(s)
  108. register char *s ;
  109. {
  110.    extern void exit() ;
  111.  
  112.    (void)fprintf(stderr, "%s\n", s) ;
  113.    if (obuffer[0]) {
  114.       (void)fprintf(stderr, "%s\n", obuffer) ;
  115.       while (param > buffer) {
  116.          (void)fprintf(stderr, " ") ;
  117.          param-- ;
  118.       }
  119.       (void)fprintf(stderr, "^\n") ;
  120.    }
  121.    if (*s == '!')
  122.       exit(1) ;
  123. }
  124.  
  125. int
  126. transform(x,y)
  127.    register int x,y ;
  128. {
  129.    register double acc ;
  130.    acc = efactor * x + slant *y ;
  131.    return (int)(acc>=0? acc+0.5 : acc-0.5 ) ;
  132. }
  133.  
  134. int
  135. getline() {
  136.    register char *p ;
  137.    register int c ;
  138.  
  139.    param = buffer ;
  140.    for (p=buffer; (c=getc(afmin)) != EOF && c != 10;)
  141.       *p++ = c ;
  142.    *p = 0 ;
  143.    (void)strcpy(obuffer, buffer) ;
  144.    if (p == buffer && c == EOF)
  145.       return(0) ;
  146.    else
  147.       return(1) ;
  148. }
  149.  
  150. char *interesting[] = { "FontName", "ItalicAngle", "IsFixedPitch",
  151.    "XHeight", "C", "KPX", "CC", "EncodingScheme", NULL} ; 
  152. #define FontName (0)
  153. #define ItalicAngle (1)
  154. #define IsFixedPitch (2)
  155. #define XHeight (3)
  156. #define C (4)
  157. #define KPX (5)
  158. #define CC (6)
  159. #define EncodingScheme (7)
  160. #define NONE (-1)
  161. int
  162. interest(s)
  163. char *s ;
  164. {
  165.    register char **p ;
  166.    register int n ;
  167.  
  168.    for (p=interesting, n=0; *p; p++, n++)
  169.       if (strcmp(s, *p)==0)
  170.          return(n) ;
  171.    return(NONE) ;
  172. }
  173.  
  174. char *
  175. mymalloc(len)
  176. int len ;
  177. {
  178.    register char *p ;
  179.    extern char *malloc() ;
  180.  
  181.    p = malloc((unsigned)len) ;
  182.    if (p==NULL)
  183.       error("! out of memory") ;
  184.    return(p) ;
  185. }
  186.  
  187. char *
  188. paramnewstring() {
  189.    register char *p, *q ;
  190.  
  191.    p = param ;
  192.    while (*p > ' ')
  193.       p++ ;
  194.    q = mymalloc((int)(p-param+1)) ;
  195.    if (*p != 0)
  196.       *p++ = 0 ;
  197.    (void)strcpy(q, param) ;
  198.    while (*p && *p <= ' ')
  199.       p++ ;
  200.    param = p ;
  201.    return(q) ;
  202. }
  203.  
  204. char *
  205. paramstring() {
  206.    register char *p, *q ;
  207.  
  208.    p = param ;
  209.    while (*p > ' ')
  210.       p++ ;
  211.    q = param ;
  212.    if (*p != 0)
  213.       *p++ = 0 ;
  214.    while (*p && *p <= ' ')
  215.       p++ ;
  216.    param = p ;
  217.    return(q) ;
  218. }
  219.  
  220. int
  221. paramnum() {
  222.    register char *p ;
  223.    int i ;
  224.  
  225.    p = paramstring() ;
  226.    if (sscanf(p, "%d", &i) != 1)
  227.       error("! integer expected") ;
  228.    return(i) ;
  229. }
  230.  
  231. float
  232. paramfloat() {
  233.    register char *p ;
  234.    float i ;
  235.  
  236.    p = paramstring() ;
  237.    if (sscanf(p, "%f", &i) != 1)
  238.       error("! number expected") ;
  239.    return(i) ;
  240. }
  241.  
  242. struct adobeinfo *
  243. newchar() {
  244.    register struct adobeinfo *ai ;
  245.  
  246.    ai = (struct adobeinfo *)mymalloc(sizeof(struct adobeinfo)) ;
  247.    ai->adobenum = -1 ;
  248.    ai->texnum = -1 ;
  249.    ai->width = -1 ;
  250.    ai->adobename = NULL ;
  251.    ai->llx = -1 ;
  252.    ai->lly = -1 ;
  253.    ai->urx = -1 ;
  254.    ai->ury = -1 ;
  255.    ai->ligs = NULL ;
  256.    ai->kerns = NULL ;
  257.    ai->pccs = NULL ;
  258.    ai->next = adobechars ;
  259.    adobechars = ai ;
  260.    return(ai) ;
  261. }
  262.  
  263. struct kern *
  264. newkern() {
  265.    register struct kern *nk ;
  266.  
  267.    nk = (struct kern *)mymalloc(sizeof(struct kern)) ;
  268.    nk->next = NULL ;
  269.    nk->succ = NULL ;
  270.    nk->delta = 0 ;
  271.    return(nk) ;
  272. }
  273.  
  274. struct pcc *
  275. newpcc() {
  276.    register struct pcc *np ;
  277.  
  278.    np = (struct pcc *)mymalloc(sizeof(struct pcc)) ;
  279.    np->next = NULL ;
  280.    np->partname = NULL ;
  281.    np->xoffset = 0 ;
  282.    np->yoffset = 0 ;
  283.    return(np) ;
  284. }
  285.  
  286. struct lig *
  287. newlig() {
  288.    register struct lig *nl ;
  289.  
  290.    nl = (struct lig *)mymalloc(sizeof(struct lig)) ;
  291.    nl->next = NULL ;
  292.    nl->succ = NULL ;
  293.    nl->sub = NULL ;
  294.    return(nl) ;
  295. }
  296.  
  297. void
  298. expect(s)
  299. char *s ;
  300. {
  301.    if (strcmp(paramstring(), s) != 0) {
  302.       (void)fprintf(stderr, "%s expected: ", s) ;
  303.       error("! syntax error") ;
  304.    }
  305. }
  306.  
  307. void
  308. handlechar() { /* an input line beginning with C */
  309.    register struct adobeinfo *ai ;
  310.    register struct lig *nl ;
  311.  
  312.    ai = newchar() ;
  313.    ai->adobenum = paramnum() ;
  314.    expect(";") ;
  315.    expect("WX") ;
  316.    ai->width = transform(paramnum(),0) ;
  317.    if (ai->adobenum >= 0) {
  318.       adobeptrs[ai->adobenum] = ai ;
  319.       cksum = (cksum<<1) ^ ai->width ;
  320.    }
  321.    expect(";") ;
  322.    expect("N") ;
  323.    ai->adobename = paramnewstring() ;
  324.    expect(";") ;
  325.    expect("B") ;
  326.    ai->llx = paramnum() ;
  327.    ai->lly = paramnum() ;
  328.    ai->llx = transform(ai->llx, ai->lly) ;
  329.    ai->urx = paramnum() ;
  330.    ai->ury = paramnum() ;
  331.    ai->urx = transform(ai->urx, ai->ury) ;
  332.    expect(";") ;
  333. /* Now look for ligatures (which aren't present in fixedpitch fonts) */
  334.    while (*param == 'L') {
  335.       expect("L") ;
  336.       nl = newlig() ;
  337.       nl->succ = paramnewstring() ;
  338.       nl->sub = paramnewstring() ;
  339.       nl->next = ai->ligs ;
  340.       ai->ligs = nl ;
  341.       expect(";") ;
  342.    }
  343.    if (strcmp(ai->adobename, "space")==0) {
  344.       fontspace = ai->width ;
  345.       nl = newlig() ;        /* space will act as zero-width Polish crossbar */
  346.       nl->succ = "l" ;       /* when used by plain TeX's \l or \L macros */
  347.       nl->sub = "lslash" ;
  348.       nl->next = ai->ligs ;
  349.       ai->ligs = nl ;
  350.       nl = newlig() ;
  351.       nl->succ = "L" ;
  352.       nl->sub = "Lslash" ;
  353.       nl->next = ai->ligs ;
  354.       ai->ligs = nl ;
  355.    } else if (strcmp(ai->adobename, "question")==0) {
  356.       nl = newlig() ;
  357.       nl->succ = "quoteleft" ;
  358.       nl->sub = "questiondown" ;
  359.       nl->next = ai->ligs ;
  360.       ai->ligs = nl ;
  361.    } else if (strcmp(ai->adobename, "exclam")==0) {
  362.       nl = newlig() ;
  363.       nl->succ = "quoteleft" ;
  364.       nl->sub = "exclamdown" ;
  365.       nl->next = ai->ligs ;
  366.       ai->ligs = nl ;
  367.    } else if (! fixedpitch) {
  368.       if (strcmp(ai->adobename, "hyphen")==0) {
  369.          nl = newlig() ;
  370.          nl->succ = "hyphen" ;
  371.          nl->sub = "endash" ;
  372.          nl->next = ai->ligs ;
  373.          ai->ligs = nl ;
  374.       } else if (strcmp(ai->adobename, "endash")==0) {
  375.          nl = newlig() ;
  376.          nl->succ = "hyphen" ;
  377.          nl->sub = "emdash" ;
  378.          nl->next = ai->ligs ;
  379.          ai->ligs = nl ;
  380.       } else if (strcmp(ai->adobename, "quoteleft")==0) {
  381.          nl = newlig() ;
  382.          nl->succ = "quoteleft" ;
  383.          nl->sub = "quotedblleft" ;
  384.          nl->next = ai->ligs ;
  385.          ai->ligs = nl ;
  386.       } else if (strcmp(ai->adobename, "quoteright")==0) {
  387.          nl = newlig() ;
  388.          nl->succ = "quoteright" ;
  389.          nl->sub = "quotedblright" ;
  390.          nl->next = ai->ligs ;
  391.          ai->ligs = nl ;
  392.       }
  393.    }
  394. }
  395.  
  396. struct adobeinfo *
  397. findadobe(p)
  398. char *p ;
  399. {
  400.    register struct adobeinfo *ai ;
  401.  
  402.    for (ai=adobechars; ai; ai = ai->next)
  403.       if (strcmp(p, ai->adobename)==0)
  404.          return(ai) ;
  405.    return(NULL) ;
  406. }
  407.  
  408. /* We ignore kerns before and after space characters, because (1) TeX
  409.  * is using the space only for Polish ligatures, and (2) TeX's
  410.  * boundarychar mechanisms are not oriented to kerns (they apply
  411.  * to both spaces and punctuation) so we don't want to use them. */
  412. void
  413. handlekern() { /* an input line beginning with KPX */
  414.    register struct adobeinfo *ai ;
  415.    register char *p ;
  416.    register struct kern *nk ;
  417.  
  418.    p = paramstring() ;
  419.    if (strcmp(p,"space")==0) return ;
  420.    ai = findadobe(p) ;
  421.    if (ai == NULL)
  422.       error("! kern char not found") ;
  423.    if (ai->adobenum<'0' || ai->adobenum>'9') {
  424. /* Ignore kerns after digits because they would mess up our nice tables */
  425.       nk = newkern() ;
  426.       nk->succ = paramnewstring() ;
  427.       if (strcmp(nk->succ,"space")==0) return ;
  428.       nk->delta = transform(paramnum(),0) ;
  429.       nk->next = ai->kerns ;
  430.       ai->kerns = nk ;
  431.     }
  432. }
  433.  
  434. void
  435. handleconstruct() { /* an input line beginning with CC */
  436.    register struct adobeinfo *ai ;
  437.    register char *p ;
  438.    register struct pcc *np ;
  439.    register int n ;
  440.    struct pcc *npp = NULL;
  441.  
  442.    p = paramstring() ;
  443.    ai = findadobe(p) ;
  444.    if (ai == NULL)
  445.       error("! composite character name not found") ;
  446.    n = paramnum() ;
  447.    expect(";") ;
  448.    while (n--) {
  449.       if (strcmp(paramstring(),"PCC") != 0) return ;
  450.         /* maybe I should expect("PCC") instead, but I'm playing it safe */
  451.       np = newpcc() ;
  452.       np->partname = paramnewstring() ;
  453.       if (findadobe(np->partname)==NULL) return ;
  454.       np->xoffset = paramnum() ;
  455.       np->yoffset = paramnum() ;
  456.       np->xoffset = transform(np->xoffset, np->yoffset) ;
  457.       if (npp) npp->next = np ;
  458.       else ai->pccs = np ;
  459.       npp = np ;
  460.       expect(";") ;
  461.    }
  462. }
  463.  
  464. void
  465. makeaccentligs() {
  466.    register struct adobeinfo *ai, *aci ;
  467.    register char *p ;
  468.    register struct lig *nl ;
  469.    for (ai=adobechars; ai; ai=ai->next) {
  470.       p = ai->adobename ;
  471.       if (strlen(p)>2)
  472.          if ((aci=findadobe(p+1)) && (aci->adobenum > 127)) {
  473.             nl = newlig() ;
  474.             nl->succ = mymalloc(2) ;
  475.             *(nl->succ + 1) = 0 ;
  476.             *(nl->succ) = *p ;
  477.             nl->sub = ai->adobename ;
  478.             nl->next = aci->ligs ;
  479.             aci->ligs = nl ;
  480.          }
  481.    }
  482. }
  483.  
  484. void
  485. readadobe() {
  486.    while (getline()) {
  487.       switch(interest(paramstring())) {
  488. case FontName:
  489.          fontname = paramnewstring() ;
  490.          break ;
  491. case EncodingScheme:
  492.          codingscheme = paramnewstring() ;
  493.          break ;
  494. case ItalicAngle:
  495.          italicangle = paramfloat() ;
  496.          break ;
  497. case IsFixedPitch:
  498.          if (*param == 't' || *param == 'T')
  499.             fixedpitch = 1 ;
  500.          else
  501.             fixedpitch = 0 ;
  502.          break ;
  503. case XHeight:
  504.          xheight = paramnum() ;
  505.          break ;
  506. case C:
  507.          handlechar() ;
  508.          break ;
  509. case KPX:
  510.          handlekern() ;
  511.          break ;
  512. case CC:
  513.          handleconstruct() ;
  514.          break ;
  515. default:
  516.          break ;
  517.       }
  518.    }
  519.    makeaccentligs() ;
  520. }
  521.  
  522. void
  523. assignchars() {
  524.    register char **p ;
  525.    register int i ;
  526.    register struct adobeinfo *ai ;
  527.    int nextfree = 128 ;
  528.  
  529. /*
  530.  *   First, we assign all those that match perfectly.
  531.  */
  532.    for (i=0, p=texencoding; i<128; i++, p++)
  533.       if ((ai=findadobe(*p)) && ai->adobenum >= 0) {
  534.          ai->texnum = i ;
  535.          texptrs[i] = ai ;
  536.       }
  537. /*
  538.  *   Next, we assign all the others whose codes are above 127, retaining
  539.  *   the adobe positions. */
  540.    for (ai=adobechars; ai; ai=ai->next)
  541.       if (ai->adobenum > 127 && ai->texnum<0) {
  542.          ai->texnum = ai->adobenum ;
  543.          texptrs[ai->adobenum] = ai ;
  544.       }
  545. /*   Finally, we map all remaining characters into free locations beginning
  546.  *   with 128, if we know how to construct those characters. */
  547.    for (ai=adobechars; ai; ai=ai->next)
  548.       if (ai->texnum<0 && (ai->adobenum>=0 || ai->pccs !=NULL)) {
  549.          while (texptrs[nextfree]) {
  550.             nextfree=(nextfree+1)&255 ;
  551.             if (nextfree==128) return ; /* all slots full */
  552.          }
  553.          ai->texnum = nextfree ;
  554.          texptrs[nextfree] = ai ;
  555.       }
  556. }
  557.  
  558. void
  559. upmap() { /* Compute uppercase mapping, when making a small caps font */
  560.    register struct adobeinfo *ai, *Ai ;
  561.    register char *p, *q ;
  562.    register struct pcc *np, *nq ;
  563.    char lwr[50] ;
  564.  
  565.    for (Ai=adobechars; Ai; Ai=Ai->next) {
  566.       p = Ai->adobename ;
  567.       if (*p>='A' && *p<='Z') {
  568.          q = lwr ;
  569.          for (; *p; p++)
  570.             *q++ = ((*p>='A' && *p<='Z') ? *p+32 : *p) ;
  571.          *q = 0;
  572.          if (ai=findadobe(lwr)) {
  573.             if (ai->texnum>=0) uppercase[ai->texnum] = Ai ;
  574.             if (Ai->texnum>=0) lowercase[Ai->texnum] = ai ;
  575.          }
  576.       }
  577.    }
  578. /* Note that, contrary to the normal true/false conventions,
  579.  * uppercase[i] is NULL and lowercase[i] is non-NULL when i is the
  580.  * ASCII code of an uppercase letter; and vice versa for lowercase letters */
  581.  
  582.    if (ai=findadobe("germandbls"))
  583.       if (Ai=findadobe("S")) { /* we also construct SS */
  584.          uppercase[ai->texnum] = ai ;
  585.          ai->width = Ai->width << 1 ;
  586.          ai->llx = Ai->llx ;
  587.          ai->lly = Ai->lly ;
  588.          ai->urx = Ai->width + Ai->urx ;
  589.          ai->ury = Ai->ury ;
  590.          ai->kerns = Ai->kerns ;
  591.          np = newpcc() ;
  592.          np->partname = "S" ;
  593.          nq = newpcc() ;
  594.          nq->partname = "S" ;
  595.          nq->xoffset = Ai->width ;
  596.          np->next = nq ;   
  597.          ai->pccs = np ;
  598.       }
  599.    if (ai=findadobe("dotlessi"))
  600.       uppercase[ai->texnum] = findadobe("I") ;
  601.    if (ai=findadobe("dotlessj"))
  602.       uppercase[ai->texnum] = findadobe("J") ;
  603. }
  604. /* The logic above seems to work well enough, but it leaves useless characters
  605.  * like `fi' and `fl' in the font if they were present initially,
  606.  * and it omits characters like `dotlessj' if they are absent initially */
  607.  
  608. /* Now we turn to computing the TFM file */
  609.  
  610. int lf, lh, nw, nh, nd, ni, nl, nk, ne, np ;
  611.  
  612. void
  613. write16(what)
  614. register short what ;
  615. {
  616.    (void)fputc(what >> 8, tfmout) ;
  617.    (void)fputc(what & 255, tfmout) ;
  618. }
  619.  
  620. void
  621. writearr(p, n)
  622. register long *p ;
  623. register int n ;
  624. {
  625.    while (n) {
  626.       write16((short)(*p >> 16)) ;
  627.       write16((short)(*p & 65535)) ;
  628.       p++ ;
  629.       n-- ;
  630.    }
  631. }
  632.  
  633. void
  634. makebcpl(p, s, n)
  635. register long *p ;
  636. register char *s ;
  637. register int n ;
  638. {
  639.    register long t ;
  640.    register long sc ;
  641.  
  642.    if (strlen(s) < n)
  643.       n = strlen(s) ;
  644.    t = ((long)n) << 24 ;
  645.    sc = 16 ;
  646.    while (n > 0) {
  647.       t |= ((long)(*(unsigned char *)s++)) << sc ;
  648.       sc -= 8 ;
  649.       if (sc < 0) {
  650.          *p++ = t ;
  651.          t = 0 ;
  652.          sc = 24 ;
  653.       }
  654.       n-- ;
  655.    }
  656.    *p++ = t ;
  657. }
  658.  
  659. int source[257] ;
  660. int unsort[257] ;
  661.  
  662. /*
  663.  *   Next we need a routine to reduce the number of distinct dimensions
  664.  *   in a TFM file. Given an array what[0]...what[oldn-1], we want to
  665.  *   group its elements into newn clusters, in such a way that the maximum
  666.  *   difference between elements of a cluster is as small as possible.
  667.  *   Furthermore, what[0]=0, and this value must remain in a cluster by
  668.  *   itself. Data such as `0 4 6 7 9' with newn=3 shows that an iterative
  669.  *   scheme in which 6 is first clustered with 7 will not work. So we
  670.  *   borrow a neat algorithm from METAFONT to find the true optimum.
  671.  *   Memory location what[oldn] is set to 0x7fffffffL for convenience.
  672.  */
  673. long nextd ; /* smallest value that will give a different mincover */
  674. int
  675. mincover(what,d) /* tells how many clusters result, given max difference d */
  676. register long d ;
  677. long *what ;
  678. {
  679.    register int m ;
  680.    register long l ;
  681.    register long *p ;
  682.  
  683.    nextd = 0x7fffffffL ;
  684.    p = what+1 ;
  685.    m = 1 ;
  686.    while (*p<0x7fffffffL) {
  687.       m++ ;
  688.       l = *p ;
  689.       while (*++p <= l+d) ;
  690.       if (*p-l < nextd) nextd = *p-l ;
  691.    }
  692.    return (m) ;
  693. }
  694.  
  695. void
  696. remap(what, oldn, newn)
  697. long *what ;
  698. int oldn, newn ;
  699. {
  700.    register int i, j ;
  701.    register long d, l ;
  702.  
  703.    what[oldn] = 0x7fffffffL ;
  704.    for (i=oldn-1; i>0; i--) {
  705.       d = what[i] ;
  706.       for (j=i; what[j+1]<d; j++) {
  707.          what[j] = what[j+1] ;
  708.          source[j] = source[j+1] ;
  709.       }
  710.       what[j] = d ;
  711.       source[j] = i ;
  712.    } /* Tom, don't let me ever catch you using bubblesort again! -- Don */
  713.  
  714.    i = mincover(what, 0L) ;
  715.    d = nextd ;
  716.    while (mincover(what,d+d)>newn) d += d ;
  717.    while (mincover(what,d)>newn) d = nextd ;
  718.  
  719.    i = 1 ;
  720.    j = 0 ;
  721.    while (i<oldn) {
  722.       j++ ;
  723.       l = what[i] ;
  724.       unsort[source[i]] = j ;
  725.       while (what[++i] <= l+d) {
  726.          unsort[source[i]] = j ;
  727.          if (i-j == oldn-newn) d = 0 ;
  728.       }
  729.       what[j] = (l+what[i-1])/2 ;
  730.    }
  731. }
  732.  
  733. /*
  734.  *   The next routine simply scales something.
  735.  *   Input is in 1000ths of an em.  Output is in FIXFACTORths of 1000.
  736.  */
  737. #define FIXFACTOR (0x100000L) /* 2^{20}, the unit fixnum */
  738. long
  739. scale(what)
  740. long what ;
  741. {
  742.    return(((what / 1000) << 20) +
  743.           (((what % 1000) << 20) + 500) / 1000) ;
  744. }
  745.  
  746. long *header, *charinfo, *width, *height, *depth, *ligkern, *kern, *tparam,
  747.      *italic ;
  748. long tfmdata[10000] ;
  749.  
  750. void
  751. buildtfm() {
  752.    register int i, j ;
  753.    register struct adobeinfo *ai ;
  754.    double tan() ;
  755.  
  756.    header = tfmdata ;
  757.    header[0] = cksum ;
  758.    header[1] = 0xa00000 ; /* 10pt design size */
  759.    makebcpl(header+2, codingscheme, 39) ;
  760.    makebcpl(header+12, fontname, 19) ;
  761.    lh = 17 ;
  762.    charinfo = header + lh ;
  763.  
  764.    for (i=0; i<256 && adobeptrs[i]==NULL; i++) ;
  765.    bc = i ;
  766.    for (i=255; i>=0 && adobeptrs[i]==NULL; i--) ;
  767.    ec = i;
  768.    if (ec < bc)
  769.       error("! no Adobe characters") ;
  770.  
  771.    width = charinfo + (ec - bc + 1) ;
  772.    width[0] = 0 ;
  773.    nw++ ;
  774.    for (i=bc; i<=ec; i++)
  775.       if (ai=adobeptrs[i]) {
  776.          width[nw]=ai->width ;
  777.          for (j=1; width[j]!=ai->width; j++) ;
  778.          ai->wptr = j ;
  779.          if (j==nw)
  780.             nw++ ;
  781.       }
  782.    if (nw>256)
  783.       error("! 256 chars with different widths") ;
  784.    depth = width + nw ;
  785.    depth[0] = 0 ;
  786.    nd = 1 ;
  787.    for (i=bc; i<=ec; i++)
  788.       if (ai=adobeptrs[i]) {
  789.          depth[nd] = -ai->lly ;
  790.          for (j=0; depth[j]!=-ai->lly; j++) ;
  791.          ai->dptr = j ;
  792.          if (j==nd)
  793.             nd++ ;
  794.       }
  795.    if (nd > 16) {
  796.       remap(depth, nd, 16) ;
  797.       nd = 16 ;
  798.       for (i=bc; i<=ec; i++)
  799.          if (ai=adobeptrs[i])
  800.             ai->dptr = unsort[ai->dptr] ;
  801.    }
  802.    height = depth + nd ;
  803.    height[0] = 0 ;
  804.    nh = 1 ;
  805.    for (i=bc; i<=ec; i++)
  806.       if (ai=adobeptrs[i]) {
  807.          height[nh]=ai->ury ;
  808.          for (j=0; height[j]!=ai->ury; j++) ;
  809.          ai->hptr = j ;
  810.          if (j==nh)
  811.             nh++ ;
  812.       }
  813.    if (nh > 16) {
  814.       remap(height, nh, 16) ;
  815.       nh = 16 ;
  816.       for (i=bc; i<=ec; i++)
  817.          if (ai=adobeptrs[i])
  818.             ai->hptr = unsort[ai->hptr] ;
  819.    }
  820.    italic  = height + nh ;
  821.    italic[0] = 0 ;
  822.    ni = 1 ;
  823.    for (i=bc; i<=ec; i++)
  824.       if (ai=adobeptrs[i]) {
  825.          italic[ni] = ai->urx - ai->width ;
  826.          if (italic[ni]<0)
  827.             italic[ni] = 0 ;
  828.          for (j=0; italic[j]!=italic[ni]; j++) ;
  829.          ai->iptr = j ;
  830.          if (j==ni)
  831.             ni++ ;
  832.       }
  833.    if (ni > 64) {
  834.       remap(italic, ni, 64) ;
  835.       ni = 64 ;
  836.       for (i=bc; i<=ec; i++)
  837.          if (ai=adobeptrs[i])
  838.             ai->iptr = unsort[ai->iptr] ;
  839.    }
  840.  
  841.    for (i=bc; i<=ec; i++)
  842.       if (ai=adobeptrs[i])
  843.          charinfo[i-bc] = ((long)(ai->wptr)<<24) +
  844.                            ((long)(ai->hptr)<<20) +
  845.                             ((long)(ai->dptr)<<16) +
  846.                              ((long)(ai->iptr)<<10) ;
  847.  
  848.    ligkern = italic + ni ;
  849.    nl = 0 ; /* ligatures and kerns omitted from raw Adobe font */
  850.    kern = ligkern + nl ;
  851.    nk = 0 ;
  852.  
  853.    newslant = (double)slant - efactor * tan(italicangle*(3.1415926535/180.0)) ;
  854.    tparam = kern + nk ;
  855.    tparam[0] = (long)(FIXFACTOR * newslant + 0.5) ;
  856.    tparam[1] = scale((long)(fontspace*efactor+0.5)) ;
  857.    tparam[2] = (fixedpitch ? 0 : scale((long)(300*efactor+0.5))) ;
  858.    tparam[3] = (fixedpitch ? 0 : scale((long)(100*efactor+0.5))) ;
  859.    tparam[4] = scale((long)xheight) ;
  860.    tparam[5] = scale((long)(1000*efactor+0.5)) ;
  861.    np = 6 ;
  862. }
  863.  
  864. void
  865. writesarr(what, len)
  866. long *what ;
  867. int len ;
  868. {
  869.    register long *p ;
  870.    int i ;
  871.  
  872.    p = what ;
  873.    i = len ;
  874.    while (i) {
  875.       *p = scale(*p) ;
  876.       p++ ;
  877.       i-- ;
  878.    }
  879.    writearr(what, len) ;
  880. }
  881.  
  882. void
  883. writetfm() {
  884.    lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np ;
  885.    write16(lf) ;
  886.    write16(lh) ;
  887.    write16(bc) ;
  888.    write16(ec) ;
  889.    write16(nw) ;
  890.    write16(nh) ;
  891.    write16(nd) ;
  892.    write16(ni) ;
  893.    write16(nl) ;
  894.    write16(nk) ;
  895.    write16(ne) ;
  896.    write16(np) ;
  897.    writearr(header, lh) ;
  898.    writearr(charinfo, ec-bc+1) ;
  899.    writesarr(width, nw) ;
  900.    writesarr(height, nh) ;
  901.    writesarr(depth, nd) ;
  902.    writesarr(italic, ni) ;
  903.    writearr(ligkern, nl) ;
  904.    writesarr(kern, nk) ;
  905.    writearr(tparam, np) ;
  906. }
  907.  
  908. /* OK, the TFM file is done! Now for our next trick, the VPL file. */
  909.  
  910. /* For TeX we want to compute a character height that works properly
  911.  * with accents. The following list of accents doesn't need to be complete. */
  912. char *accents[] = { "acute", "tilde", "caron", "dieresis", NULL} ;
  913. int
  914. texheight(ai)
  915. register struct adobeinfo *ai ;
  916. {
  917.    register char **p;
  918.    register struct adobeinfo *aci, *acci ;
  919.    if (*(ai->adobename + 1)) return (ai->ury) ; /* that was the simple case */
  920.    for (p=accents; *p; p++)  /* otherwise we look for accented letters */
  921.       if (aci=findadobe(*p)) {
  922.          strcpy(buffer,ai->adobename) ;
  923.          strcat(buffer,*p) ;
  924.          if (acci=findadobe(buffer)) return (acci->ury - aci->ury + xheight) ;
  925.       }
  926.    return (ai->ury) ;
  927. }
  928.  
  929. /* modified tgr to eliminate varargs problems */
  930.  
  931. #define vout(s)  fprintf(vplout, s)
  932. int level ; /* the depth of parenthesis nesting in VPL file being written */
  933.  
  934. void vlevout() {
  935.    register int l = level ;
  936.    while (l--) vout("   ") ;
  937. }
  938.  
  939. void vlevnlout() {
  940.    vout("\n") ;
  941.    vlevout() ;
  942. }
  943.  
  944. #define voutln(str) {fprintf(vplout,"%s\n",str);vlevout();}
  945. /*
  946. void
  947. voutln(s)
  948. char *s ;
  949. {
  950.    register char l ;
  951.    fprintf(vplout,"%s\n", s) ;
  952.    for (l=level; l; l--) vout("   ") ;
  953. }
  954. */
  955. #define voutln2(f,s) {fprintf(vplout,f,s);vlevnlout();}
  956. /*
  957. void
  958. voutln2(f,s)
  959. char *f, *s ;
  960. {
  961.    register char l ;
  962.    (void) sprintf(buffer, f, s) ;
  963.    fprintf(vplout,"%s\n", buffer) ;
  964.    for (l=level; l; l--) vout("   ") ;
  965. }
  966. */
  967. #define voutln3(f,a,b) {fprintf(vplout,f,a,b);vlevnlout();}
  968. /*
  969. void
  970. voutln3(f,s1,s2)
  971. char *f, *s1, *s2 ;
  972. {
  973.    register char l ;
  974.    (void) sprintf(buffer, f, s1, s2) ;
  975.    fprintf(vplout,"%s\n", buffer) ;
  976.    for (l=level; l; l--) vout("   ") ;
  977. }
  978. */
  979. void
  980. vleft()
  981. {
  982.    level++ ;
  983.    vout("(") ;
  984. }
  985.  
  986. void
  987. vright()
  988. {
  989.    level-- ;
  990.    voutln(")") ;
  991. }
  992.  
  993. char vcharbuf[6] ;
  994. char *vchar(c)
  995. int c ;
  996. {
  997.    if ((c>='0' && c<='9')||(c>='A' && c<='Z')||(c>='a' && c<='z'))
  998.       (void) sprintf(vcharbuf,"C %c", c) ;
  999.    else (void) sprintf(vcharbuf,"O %o", c) ;
  1000.    return (vcharbuf) ;
  1001. }
  1002.  
  1003. void
  1004. writevpl()
  1005. {
  1006.    register int i ;
  1007.    register struct adobeinfo *ai ;
  1008.    register struct lig *nlig ;
  1009.    register struct kern *nkern ;
  1010.    register struct pcc *npcc ;
  1011.    struct adobeinfo *asucc, *asub, *api ;
  1012.    int xoff, yoff, ht ;
  1013.    char unlabeled ;
  1014.    double tan() ;
  1015.  
  1016.    voutln2("(VTITLE Created by %s)", titlebuf) ;
  1017.    voutln("(COMMENT Please edit that VTITLE if you edit this file)") ;
  1018.    (void)sprintf(obuffer, "TeX-%s%s%s%s", outname,
  1019.       (efactor==1.0? "" : "-E"), (slant==0.0? "" : "-S"),
  1020.                  (makevpl==1? "" : "-CSC")) ;
  1021.    if (strlen(obuffer)>19) { /* too long, will retain first 9 and last 10 */
  1022.       register char *p, *q ;
  1023.       for (p = &obuffer[9], q = &obuffer[strlen(obuffer)-10] ; p<&obuffer[19];
  1024.               p++, q++) *p = *q ;
  1025.       obuffer[19] = '\0' ;
  1026.    }
  1027.    voutln2("(FAMILY %s)" , obuffer) ;
  1028.    voutln2("(CODINGSCHEME TeX text + %s)", codingscheme) ;
  1029.    voutln("(DESIGNSIZE R 10.0)") ;
  1030.    voutln("(DESIGNUNITS R 1000)") ;
  1031.    voutln("(COMMENT DESIGNSIZE (1 em) IS IN POINTS)") ;
  1032.    voutln("(COMMENT OTHER DIMENSIONS ARE MULTIPLES OF DESIGNSIZE/1000)") ;
  1033.    voutln2("(CHECKSUM O %lo)",cksum ^ 0xffffffff) ;
  1034.    vleft() ; voutln("FONTDIMEN") ;
  1035.    if (newslant)
  1036.       voutln2("(SLANT R %f)", newslant) ;
  1037.    voutln2("(SPACE D %d)", transform(fontspace,0)) ;
  1038.    if (! fixedpitch) {
  1039.       voutln2("(STRETCH D %d)", transform(200,0)) ;
  1040.       voutln2("(SHRINK D %d)", transform(100,0)) ;
  1041.    }
  1042.    voutln2("(XHEIGHT D %d)", xheight) ;
  1043.    voutln2("(QUAD D %d)", transform(1000,0)) ;
  1044.    voutln2("(EXTRASPACE D %d)", transform(fixedpitch ? fontspace : 111, 0)) ;
  1045.    vright() ;
  1046.    vleft() ; voutln("MAPFONT D 0");
  1047.    voutln2("(FONTNAME %s)", outname) ;
  1048.    voutln2("(FONTCHECKSUM O %lo)", cksum) ;
  1049.    vright() ;
  1050.    if (makevpl>1) {
  1051.       vleft() ; voutln("MAPFONT D 1");
  1052.       voutln2("(FONTNAME %s)", outname) ;
  1053.       voutln("(FONTAT D 800)") ;
  1054.       voutln2("(FONTCHECKSUM O %lo)", cksum) ;
  1055.       vright() ;
  1056.    }
  1057.  
  1058.    for (i=0; i<256 && texptrs[i]==NULL; i++) ;
  1059.    bc = i ;
  1060.    for (i=255; i>=0 && texptrs[i]==NULL; i--) ;
  1061.    ec = i;
  1062.  
  1063.    vleft() ; voutln("LIGTABLE") ;
  1064.    for (i=bc; i<=ec; i++)
  1065.       if (ai=texptrs[i]) {
  1066.          unlabeled = 1 ;
  1067.          if (uppercase[i]==NULL) /* omit ligatures from smallcap lowercase */
  1068.             for (nlig=ai->ligs; nlig; nlig=nlig->next)
  1069.                if (asucc=findadobe(nlig->succ))
  1070.                   if (asucc->texnum>=0)
  1071.                      if (asub=findadobe(nlig->sub))
  1072.                         if (asub->texnum>=0) {
  1073.                            if (unlabeled) {
  1074.                               voutln2("(LABEL %s)", vchar(i)) ;
  1075.                               unlabeled = 0 ;
  1076.                            }
  1077.                            voutln3("(LIG %s O %o)", vchar(asucc->texnum),
  1078.                                 asub->texnum) ;
  1079.                         }
  1080.          for (nkern = (uppercase[i] ? uppercase[i]->kerns : ai->kerns);
  1081.                     nkern; nkern=nkern->next)
  1082.             if (asucc=findadobe(nkern->succ))
  1083.                if (asucc->texnum>=0)
  1084.                   if (uppercase[asucc->texnum]==NULL) {
  1085.                      if (unlabeled) {
  1086.                         voutln2("(LABEL %s)", vchar(i)) ;
  1087.                         unlabeled = 0 ;
  1088.                      }
  1089.                      if (uppercase[i]) {
  1090.                         if (lowercase[asucc->texnum]) {
  1091.                            voutln3("(KRN %s R %.1f)",
  1092.                                  vchar(lowercase[asucc->texnum]->texnum),
  1093.                                  0.8*nkern->delta) ;
  1094.                         } else voutln3("(KRN %s R %.1f)",
  1095.                                  vchar(asucc->texnum), 0.8*nkern->delta) ;
  1096.                      } else {
  1097.                         voutln3("(KRN %s R %d)", vchar(asucc->texnum),
  1098.                                 nkern->delta) ;
  1099.                         if (lowercase[asucc->texnum])
  1100.                            if (lowercase[asucc->texnum]->texnum>=0)
  1101.                               voutln3("(KRN %s R %.1f)",
  1102.                                 vchar(lowercase[asucc->texnum]->texnum),
  1103.                                 0.8*nkern->delta) ;
  1104.                      }
  1105.                   }
  1106.          if (! unlabeled) voutln("(STOP)") ;
  1107.       }
  1108.    vright() ;
  1109.  
  1110.    for (i=bc; i<=ec; i++)
  1111.       if (ai=texptrs[i]) {
  1112.          vleft() ; fprintf(vplout, "CHARACTER %s", vchar(i)) ;
  1113.          if (*vcharbuf=='C') {
  1114.             voutln("") ;
  1115.          } else
  1116.             voutln2(" (comment %s)", ai->adobename) ;
  1117.          if (uppercase[i]) {
  1118.             ai=uppercase[i] ;
  1119.             voutln2("(CHARWD R %.1f)", 0.8 * (ai->width)) ;
  1120.             if (ht=texheight(ai)) voutln2("(CHARHT R %.1f)", 0.8 * ht) ;
  1121.             if (ai->lly) voutln2("(CHARDP R %.1f)", -0.8 * ai->lly) ;
  1122.             if (ai->urx > ai->width)
  1123.                voutln2("(CHARIC R %.1f)", 0.8 * (ai->urx - ai->width)) ;
  1124.          } else {
  1125.             voutln2("(CHARWD R %d)", ai->width) ;
  1126.             if (ht=texheight(ai)) voutln2("(CHARHT R %d)", ht) ;
  1127.             if (ai->lly) voutln2("(CHARDP R %d)", -ai->lly) ;
  1128.             if (ai->urx > ai->width)
  1129.                voutln2("(CHARIC R %d)", ai->urx - ai->width) ;
  1130.          }
  1131.          if (ai->adobenum != i || uppercase[i]) {
  1132.             vleft() ; voutln("MAP") ;
  1133.             if (uppercase[i]) voutln("(SELECTFONT D 1)") ;
  1134.             if (ai->pccs) {
  1135.                xoff = 0 ; yoff = 0 ;
  1136.                for (npcc = ai->pccs; npcc; npcc=npcc->next)
  1137.                   if (api=findadobe(npcc->partname))
  1138.                      if (api->texnum>=0) {
  1139.                         if (npcc->xoffset != xoff) {
  1140.                            if (uppercase[i]) {
  1141.                               voutln2("(MOVERIGHT R %.1f)",
  1142.                                       0.8 * (npcc->xoffset - xoff)) ;
  1143.                            } else voutln2("(MOVERIGHT R %d)",
  1144.                                       npcc->xoffset - xoff) ;
  1145.                            xoff = npcc->xoffset ;
  1146.                         }
  1147.                         if (npcc->yoffset != yoff) {
  1148.                            if (uppercase[i]) {
  1149.                               voutln2("(MOVEUP R %.1f)",
  1150.                                       0.8 * (npcc->yoffset - yoff)) ;
  1151.                            } else voutln2("(MOVEUP R %d)",
  1152.                                       npcc->yoffset - yoff) ;
  1153.                            yoff = npcc->yoffset ;
  1154.                         }
  1155.                         voutln2("(SETCHAR O %o)", api->adobenum) ;
  1156.                         xoff += texptrs[api->texnum]->width ;
  1157.                      }
  1158.             } else voutln2("(SETCHAR O %o)", ai->adobenum) ;
  1159.             vright() ;
  1160.          }
  1161.          vright() ;
  1162.       }
  1163.    if (level) error("! I forgot to match the parentheses") ;
  1164. }
  1165.  
  1166. void
  1167. openfiles(argc, argv)
  1168. int argc ;
  1169. char *argv[] ;
  1170. {
  1171.    register int lastext ;
  1172.    register int i ;
  1173.    extern void exit() ;
  1174.    tfmout = (FILE *)NULL ;
  1175.  
  1176.    if (argc == 1) {
  1177.       (void)printf("afm2tfm 5.36, Copyright 1990 by Radical Eye Software\n") ;
  1178.       (void)
  1179.         printf("Usage: afm2tfm foo[.afm] [-v|-V bar[.vpl]] [foo[.tfm]]\n") ;
  1180.      exit(0) ;
  1181.    }
  1182.  
  1183.    (void)sprintf(titlebuf, "%s %s", argv[0], argv[1]) ;
  1184.    (void)strcpy(inname, argv[1]) ;
  1185.    lastext = -1 ;
  1186.    for (i=0; inname[i]; i++)
  1187.       if (inname[i] == '.')
  1188.          lastext = i ;
  1189.       else if (inname[i] == '/' || inname[i] == ':')
  1190.          lastext = -1 ;
  1191.    if (lastext == -1) (void)strcat(inname, ".afm") ;
  1192.    if ((afmin=fopen(inname, "r"))==NULL)
  1193.       error("! can't open afm input file") ;
  1194.  
  1195.    while (argc>3 && *argv[2]=='-') {
  1196.       switch (argv[2][1]) {
  1197. case 'V': makevpl++ ;
  1198. case 'v': makevpl++ ;
  1199.          (void)strcpy(outname, argv[3]) ;
  1200.          lastext = -1 ;
  1201.          for (i=0; outname[i]; i++)
  1202.             if (outname[i] == '.')
  1203.                lastext = i ;
  1204.             else if (outname[i] == '/' || outname[i] == ':')
  1205.                lastext = -1 ;
  1206.          if (lastext == -1) (void)strcat(outname, ".vpl") ;
  1207.          if ((vplout=fopen(outname, "w"))==NULL)
  1208.             error("! can't open vpl output file") ;
  1209.          break ;
  1210. case 'e': if (sscanf(argv[3], "%f", &efactor)==0 || efactor<0.01)
  1211.             error("! Bad extension factor") ;
  1212.          break ;
  1213. case 's': if (sscanf(argv[3], "%f", &slant)==0)
  1214.             error("! Bad slant parameter") ;
  1215.          break ;
  1216. case 't':  /* get tfm name if present */
  1217.          (void)strcpy(outname, argv[3]) ;
  1218.          lastext = -1 ;
  1219.          for (i=0; outname[i]; i++)
  1220.             if (outname[i] == '.')
  1221.                lastext = i ;
  1222.             else if (outname[i] == '/' || outname[i] == ':')
  1223.                     lastext = -1 ;
  1224.         if (lastext == -1) {
  1225.            lastext = strlen(outname) ;
  1226.            (void)strcat(outname, ".tfm") ;
  1227.         }
  1228.         if (tfmout == NULL && (tfmout=fopen(outname, "w"))==NULL)
  1229.            error("! can't open tfm output file") ;
  1230.         break ;
  1231. default: (void)fprintf(stderr, "Unknown option %s %s will be ignored.\n",
  1232.                          argv[2], argv[3]) ;
  1233.       }
  1234.       (void)sprintf(titlebuf, "%s %s %s", titlebuf, argv[2], argv[3]) ;
  1235.       argv += 2 ;
  1236.       argc -= 2 ;
  1237.    }
  1238.  
  1239.    if (argc>3 || (argc==3 && *argv[2]=='-'))
  1240.       error("! Usage: afm2tfm foo[.afm] [-v|-V bar[.vpl]] [foo[.tfm]]") ;
  1241.          
  1242.    if (argc == 2) (void)strcpy(outname, inname) ;
  1243.    else (void)strcpy(outname, argv[2]) ;
  1244.  
  1245.    lastext = -1 ;
  1246.    for (i=0; outname[i]; i++)
  1247.       if (outname[i] == '.')
  1248.          lastext = i ;
  1249.       else if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\')
  1250.          lastext = -1 ;
  1251.    if (argc == 2) {
  1252.       outname[lastext] = 0 ;
  1253.       lastext = -1 ;
  1254.    }
  1255.    if (lastext == -1) {
  1256.       lastext = strlen(outname) ;
  1257.       (void)strcat(outname, ".tfm") ;
  1258.    }
  1259.    if ((tfmout=fopen(outname, "w"))==NULL)
  1260.       error("! can't open tfm output file") ;
  1261.    outname[lastext] = 0 ;
  1262. /*
  1263.  *   Now we strip off any directory information, so we only use the
  1264.  *   base name in the vf file.  We accept any of /, :, or \ as directory
  1265.  *   delimiters, so none of these are available for use inside the
  1266.  *   base name; this shouldn't be a problem.
  1267.  */
  1268.    for (i=0, lastext=0; outname[i]; i++)
  1269.       if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\')
  1270.          lastext = i + 1 ;
  1271.    if (lastext)
  1272.       strcpy(outname, outname + lastext) ;
  1273. }
  1274.  
  1275. #ifndef VMS
  1276. void
  1277. #endif
  1278. main(argc, argv)
  1279. int argc ;
  1280. char *argv[] ;
  1281. {
  1282.    extern void exit() ;
  1283.  
  1284.    openfiles(argc, argv) ;
  1285.    readadobe() ;
  1286.    buildtfm() ;
  1287.    writetfm() ;
  1288.    if (makevpl) {
  1289.       assignchars() ;
  1290.       if (makevpl>1) upmap() ;
  1291.       writevpl() ;
  1292.    }
  1293.    exit(0) ;
  1294.    /*NOTREACHED*/
  1295. }
  1296.